/*
 * BufferImpl.h
 *
 *  Created on: 2011/05/29
 *      Author: naoki
 */

#ifndef INCLUDED_NTENGINE_BUFFERIMPL_H
#define INCLUDED_NTENGINE_BUFFERIMPL_H

#include "NTEngine/NTEngine.h"
#include "NTEngine/Base/Impl/BufferImpl.h"

namespace NTEngine {

template<class T> inline Buffer<T>::Buffer( int blockSize )
: m_BlockSize( blockSize )
, m_Size( 0 )
, m_LastPos( blockSize )
, m_CurrentPos( blockSize )
, m_LastBlock( &m_Head)
, m_CurrentBlock( &m_Head ) {
	m_Head.m_Next = &m_Head;
	m_Head.m_Elements = 0;
}

template<class T> inline Buffer<T>::~Buffer() {
	clear();
}

template<class T> inline void Buffer<T>::set( int blockSize ) {
	ASSERT( ( m_Size == 0 ) && "Not Empty." );
	if ( m_Size > 0 ) {
		clear();
	}
	m_BlockSize = blockSize;
	m_LastPos = blockSize;
}

template<class T> inline int Buffer<T>::size() const {
	return m_Size;
}

template<class T> inline void Buffer<T>::clear() {
	Block* n = m_Head.m_Next;
	while ( n != &m_Head ) {
		Block* next = n->m_Next;
		int destructN = ( m_LastBlock == n ) ? m_LastPos : m_BlockSize;
		for ( int i = 0; i < destructN; ++i ) {
			n->m_Elements[ i ].~T();
		}
		OPERATOR_DELETE( n->m_Elements );
		n->m_Next = 0;
		SAFE_DELETE( n );
		n = next;
	}
	m_Size = 0;
	m_Head.m_Next = m_LastBlock = m_CurrentBlock = &m_Head;
	m_LastPos = m_CurrentPos = m_BlockSize;
}

template<class T> inline T* Buffer<T>::add() {
	if ( m_LastPos == m_BlockSize ) {
		m_LastBlock->m_Next = NEW Block;
		if ( m_LastBlock == &m_Head ) {
			m_CurrentBlock = m_LastBlock->m_Next;
			m_CurrentPos = 0;
		}
		m_LastBlock = m_LastBlock->m_Next;
		m_LastPos = 0;
		m_LastBlock->m_Next = &m_Head;
		m_LastBlock->m_Elements = static_cast<T*>( OPERATOR_NEW( sizeof(T) * m_BlockSize ) );
	}
	T* r = new ( &m_LastBlock->m_Elements[ m_LastPos ] ) T;
	++m_LastPos;
	++m_Size;
	return r;
}

template<class T> inline void Buffer<T>::add( const T& v ) {
	if ( m_LastPos == m_BlockSize ) {
		m_LastBlock->m_Next = NEW Block;
		if ( m_LastBlock == &m_Head ) {
			m_CurrentBlock = m_LastBlock->m_Next;
			m_CurrentPos = 0;
		}
		m_LastBlock = m_LastBlock->m_Next;
		m_LastPos = 0;
		m_LastBlock->m_Next = &m_Head;
		m_LastBlock->m_Elements = static_cast<T*>( OPERATOR_NEW( sizeof(T) * m_BlockSize ) );
	}
	new ( &m_LastBlock->m_Elements[ m_LastPos ] ) T( v );
	++m_LastPos;
	++m_Size;
}

template<class T> inline void Buffer<T>::toNext() {
	if ( m_CurrentPos == m_LastBlock ) {
		++m_CurrentPos;
		if ( m_CurrentPos == m_LastPos ) {
			m_CurrentBlock = &m_Head;
		}
	}
	else {
		++m_CurrentPos;
		if ( m_CurrentPos == m_BlockSize ) {
			m_CurrentBlock = m_CurrentBlock->m_Next;
			m_CurrentPos = 0;
		}
	}
}

template<class T> inline bool Buffer<T>::isEnd() const {
	return ( m_CurrentBlock == &m_Head );
}

template<class T> inline void Buffer<T>::rewind() {
	m_CurrentBlock = m_Head.m_Next;
	m_CurrentPos = 0;
}

template<class T> inline T* Buffer<T>::get() {
	ASSERT( ( m_CurrentBlock != m_LastBlock ) || ( m_CurrentPos < m_LastPos ) && "exceeded the range" );
	return &m_CurrentBlock->m_Elements[ m_CurrentPos ];
}

template<class T> inline const T* Buffer<T>::get() const {
	ASSERT( ( m_CurrentBlock != m_LastBlock ) || ( m_CurrentPos < m_LastPos ) && "exceeded the range" );
	return &m_CurrentBlock->m_Elements[ m_CurrentPos ];
}

template<class T> inline void Buffer<T>::copyTo( T* out ) const {
	int pos = 0;
	Block* n = m_Head.m_Next;
	while ( n != &m_Head ) {
		Block* next = n->m_Next;
		int elementN = ( m_LastBlock == n ) ? m_LastPos : m_BlockSize;
		for ( int i = 0; i < elementN; ++i ) {
			out[ pos ] = n->m_Elements[ i ];
			++pos;
		}
		n = next;
	}
}

template<class T> inline void Buffer<T>::copyTo( Array<T>* out ) const {
	out->setSize( m_Size );
	if ( m_Size > 0 ) {
		copyTo( &( (*out)[ 0 ] ) );
	}
}

}

#endif // INCLUDED_NTENGINE_BUFFERIMPL_H
